home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacHack 1994
/
MacHack 1994.toast
/
MacHack™94
/
Talks & Papers
/
Michael D. Crawford↵
/
Word Services SDK 1.0.5
/
Writeswell Jr. Source
/
DoChecking.c
< prev
next >
Wrap
Text File
|
1993-03-17
|
14KB
|
575 lines
/* DoChecking.c
* Functions that actually do the calls to the Word Services servers.
* ©1992 Working Software, Inc.
* This source code is copyrighted. Permission is granted to use the Word Services
* portion of the Writeswell Jr. source code in your own programs, but you
* may not distribute the Writeswell Jr. word-processor code as a
* commercial product. If you modify the code, please do not call it
* Writeswell Jr. (or Writeswell.) This will ensure that people understand the
* program and don’t have to deal with a number of different versions with
* who-knows-what going on in the code.
*
* Writeswell Jr. and Writeswell are trademarks of Working Software, Inc.
* 28 Dec 91 Mike Crawford
*/
#include <Aliases.h>
#include <Processes.h>
#include <AppleEvents.h>
#include <AEObjects.h>
#include <AEPackObject.h>
#include <AERegistry.h>
#include "TBConstants.h"
#include "TBGlobals.h"
#include "WordServices.h"
#include "AppEvents.h"
#include "ObWind.h"
#include "ObText.h"
#include "Gripe.h"
#include "Prefs.h"
#include "DoChecking.h"
#include "FindProcess.h"
#include "TableCheck.h"
#define LAUNCH_BY_EVENT /* Undef this to use Proc Mgr instead of Finder AE */
#define kLaunchTimeout 1800 /* If the speller's on a floppy, it may take a while */
#define kBatchTimeout 600
OSErr LaunchSpeller( AliasHandle aliasHdl );
OSErr DoSpellCheck( short serviceNumber )
{
OSErr err;
AEDesc docSpecifier;
AEDesc textDescriptor;
AEDesc textSpecifier;
AEAddressDesc spellerAddr;
AppleEvent btchEvent;
AppleEvent replyEvent;
WWJrPrefsHdl prefHdl;
ServiceType servType;
prefHdl = GetPrefHandle();
if ( !prefHdl ){
Gripe( "\pCannot get preferences handle" );
return resNotFound;
}
err = OpenSpeller( serviceNumber, &spellerAddr, &servType );
if ( err ){
Gripe( "\pOpenSpeller failed to connect with speller" );
return err;
}
switch ( servType ){
case kBatchService:
if ( (*prefHdl)->sendByList ){
err = DoBatchCheck( &spellerAddr );
if ( err ){
Gripe( "\pDoBatchCheck failed" );
return err;
}
}else{
err = DoBatchTableCheck( &spellerAddr );
if ( err ){
Gripe( "\pDoBatchTableCheck failed" );
return err;
}
}
break;
case kInteractiveService:
Gripe( "\pInteractive service is not implemented yet" );
return noErr;
break;
default:
Gripe( "\pBad service type code in preferences file" );
return noErr;
}
return noErr;
}
OSErr DoBatchCheck( AEAddressDesc *spellerAddrPtr )
{
OSErr err;
AEDesc textSpecifier;
AppleEvent btchEvent;
AppleEvent replyEvent;
WWJrPrefsHdl prefHdl;
AEDescList textSpecList; /* 1.0d7 */
long index; /* 1.0d7 */
/* We make an object specifier that refers to _our_own_ window
*/
err = CreateTextSpecifier( 1L, 1L, &textSpecifier );
if ( err ){
Gripe( "\pCreateTextSpecifier failed" );
return err;
}
prefHdl = GetPrefHandle();
if ( !prefHdl ){
Gripe( "\pCannot get preferences handle" );
return resNotFound;
}
if ( (*prefHdl)->checkSel ){
/* Make a formRange descriptor that gives the selection range */
Gripe( "\pSelection-only checking is not yet implemented" );
return noErr;
}
/* Create the event to send to the speller */
err = AECreateAppleEvent( kWordServicesClass,
kWSBatchCheckMe,
spellerAddrPtr,
kAutoGenerateReturnID,
kAnyTransactionID,
&btchEvent );
if ( err ){
Gripe( "\pcreate btch event failed" );
return err;
}
err = AEDisposeDesc( spellerAddrPtr );
if ( err ){
Gripe( "\pAEDisposeDesc failed" );
return err;
}
#ifdef OLD_SPEC
/* This was used until 1.0d6 */
/* Insert the object specifier as the direct object of the batch event */
err = AEPutParamDesc( &btchEvent,
keyDirectObject,
&textSpecifier );
if ( err ){
Gripe( "\pAEPutParamDesc failed to put direct object on batch event" );
return err;
}
err = AEDisposeDesc( &textSpecifier );
if ( err ){
Gripe( "\pAEDisposeDesc failed" );
return err;
}
#else
/* 1.0d7 This is the new way to do it - use a list of object specifiers (even if
* only one of them
*/
err = AECreateList( (Ptr)NULL, (Size)0, false, &textSpecList );
if ( err ){
Gripe( "\pAECreateList failed" );
return err;
}
/* Put our specifier into it. If we had multiple text blocks to check, we would
* put specifiers for each into it in turn.
* Note that we use AEPutDesc, not AEPutParamDesc, to put items into an indexed
* list.
*/
#define N_TEXT_SPECS 1L /*3L */ /* Define this to be 1 for normal checking */
for ( index = 1L; index <= N_TEXT_SPECS; index++ ){
err = AEPutDesc( &textSpecList,
index,
&textSpecifier );
if ( err ){
Gripe( "\pAEPutDesc failed to put text specifier into textSpecList" );
return err;
}
}
err = AEDisposeDesc( &textSpecifier );
if ( err ){
Gripe( "\pAEDisposeDesc failed" );
return err;
}
/* Insert the list of object specifiers as the direct object of the batch event */
err = AEPutParamDesc( &btchEvent,
keyDirectObject,
&textSpecList );
if ( err ){
Gripe( "\pAEPutParamDesc failed to put direct object on batch event" );
return err;
}
/* 1.0.3 MDC fixed a bug in which we disposed of textSpecifier a second
* time, instead of properly disposing of textSpecList
*/
err = AEDisposeDesc( &textSpecList );
if ( err ){
Gripe( "\pAEDisposeDesc failed" );
return err;
}
#endif
/* Send the event. We await the reply, so that if there is a failure of some
* sort in the initial connection, we can alert the user right away. The timeout
* value to use here should be as long as one would care to have a user wait for
* the completion of a menu command. Since we expect that the speller is on a local
* machine in this case, and should be able to respond immediately, we just give
* a few seconds for the timeout.
*
* We should assign an idle proc to spin the cursor. Even better would be a progress
* dialog that says "Contacting speller" or some such, with an animated display that
* shows the time elapsed relative to the total timeout, so the user will know how
* long she may have to wait
*/
err = AESend( &btchEvent,
&replyEvent,
kAEWaitReply + kAECanInteract + kAECanSwitchLayer,
kAENormalPriority,
kBatchTimeout,
(IdleProcPtr)NULL,
(EventFilterProcPtr)NULL );
if ( err ){
Gripe( "\psend batch event failed" );
return err;
}
err = AEDisposeDesc( &btchEvent );
if ( err ){
Gripe( "\pAEDisposeDesc failed" );
return err;
}
/* MDC 1.1.1 fix a memory leak */
err = AEDisposeDesc( &replyEvent );
if ( err ){
Gripe( "\pAEDisposeDesc failed" );
return err;
}
/* Now the event has been sent. There is nothing more that we have to actually do
* on our own initiative to accomplish the spelling; we just sit back and respond
* to events. In particular, we don't remember that spelling is taking place - once
* the spelling is done, we will just start seeing events from the user (mouse and
* key clicks and so on
*/
return noErr;
}
OSErr OpenSpeller( short serviceNumber,
AEAddressDesc *spellerAddrPtr,
ServiceType *servTypePtr )
{
WWJrPrefsHdl prefHdl;
short servID;
AliasHandle aliasHdl;
short curFile;
OSType signature;
ProcessSerialNumber psn;
ProcessInfoRec pInfo;
OSErr err;
prefHdl = GetPrefHandle();
if ( !prefHdl ){
Gripe( "\pCannot get prefs handle" );
return resNotFound;
}
servID = gServItemID[ serviceNumber - 1 ]; /* C arrays start at 0 */
*servTypePtr = (*prefHdl)->serviceType[ servID - kServiceBaseID ];
curFile = CurResFile();
UseResFile( gPrefFileRefNum );
aliasHdl = (AliasHandle)GetResource( rAliasType, servID );
UseResFile( curFile );
if ( !aliasHdl ){
Gripe( "\pCannot get alias handle for service" );
return resNotFound;
}
/* See if the speller is out there */
signature = (*aliasHdl)->userType;
if ( !FindAProcess( signature, &psn, &pInfo, (FSSpecPtr)NULL, (StringPtr)NULL ) ){
EventRecord event;
short i;
err = LaunchSpeller( aliasHdl );
if ( err ){
Gripe( "\pUnable to launch Word Services server" );
return err;
}
#ifdef NEVER
/* Got to sleep for a little while - otherwise the speller croaks */
for ( i = 0; i < 10; i++ )
WaitNextEvent( 0, &event, 1, (RgnHandle)NULL );
#endif
}
err = AECreateDesc( typeApplSignature,
(Ptr)&signature,
sizeof( signature ),
spellerAddrPtr );
if ( err ){
Gripe( "\pAECreateDesc failed" );
return err;
}
return noErr;
}
#ifdef LAUNCH_BY_EVENT
OSErr LaunchSpeller( AliasHandle aliasHdl )
{
OSErr err;
FSSpec spellerSpec;
Boolean changed;
AppleEvent launchEvent;
AppleEvent replyEvent;
AEDesc aliasDesc;
AEDesc folderDesc;
FSSpec folderSpec;
AEDesc finderAddr;
DescType finderSig;
AliasHandle fAliasHdl;
AEDescList aliasList;
/* Make sure the speller can still be found, and get its spec */
err = ResolveAlias( (FSSpecPtr)NULL, aliasHdl, &spellerSpec, &changed );
if ( err ){
Gripe( "\pCannot locate speller" ); /* Might be user canceled AShare */
return err;
}
if ( changed ){
ChangedResource( aliasHdl );
WriteResource( aliasHdl );
}
/* make an alias for the parent folder */
err = FSMakeFSSpec( spellerSpec.vRefNum,
spellerSpec.parID,
(StringPtr)NULL,
&folderSpec );
if ( err ){
Gripe( "\pMakeFSSpec failed" );
return err;
}
err = NewAlias( (FSSpecPtr)NULL, &folderSpec, &fAliasHdl );
if ( err ){
Gripe( "\pNewAlias failed" );
return err;
}
/* Create the event to send to the Finder */
finderSig = 'MACS'; /* Creator code of finder; type is 'FNDR' */
err = AECreateDesc( typeApplSignature,
(Ptr)&finderSig,
sizeof( finderSig ),
&finderAddr );
if ( err ){
Gripe( "\pAECreateDesc failed" );
return err;
}
err = AECreateAppleEvent( kAEFinderEvents,
kAEOpenSelection,
&finderAddr,
kAutoGenerateReturnID,
kAnyTransactionID,
&launchEvent );
if ( err ){
Gripe( "\pcreate open selection event failed" );
return err;
}
err = AEDisposeDesc( &finderAddr );
if ( err ){
Gripe( "\pAEDisposeDesc failed" );
return err;
}
/* Insert the folder alias record as the direct object of the batch event */
/* First make a descriptor of the alias */
HLock( fAliasHdl );
err = AECreateDesc( typeAlias,
(Ptr)*fAliasHdl,
(*fAliasHdl)->aliasSize,
&folderDesc );
HUnlock( fAliasHdl );
DisposHandle( fAliasHdl );
if ( err ){
Gripe( "\pAECreateDesc failed" );
return err;
}
err = AEPutParamDesc( &launchEvent,
keyDirectObject,
&folderDesc );
if ( err ){
Gripe( "\pAEPutParamDesc failed to put direct object on open selection event" );
return err;
}
err = AEDisposeDesc( &folderDesc );
if ( err ){
Gripe( "\pAEDisposeDesc failed" );
return err;
}
/* Put the application alias on the event */
HLock( aliasHdl );
err = AECreateDesc( typeAlias,
(Ptr)*aliasHdl,
(*aliasHdl)->aliasSize,
&aliasDesc );
HUnlock( aliasHdl );
if ( err ){
Gripe( "\pAECreateDesc failed" );
return err;
}
/* Now make a list of the alias descriptor. There is only one element in this
* list
*/
err = AECreateList( (Ptr)NULL, (Size)0, false, &aliasList );
if ( err ){
Gripe( "\pAECreateList failed" );
return err;
}
err = AEPutDesc( &aliasList, 1L, &aliasDesc );
if ( err ){
Gripe( "\pAEPutDesc failed to put alias into alias list" );
return err;
}
err = AEDisposeDesc( &aliasDesc );
if ( err ){
Gripe( "\pAEDisposeDesc failed" );
return err;
}
err = AEPutParamDesc( &launchEvent,
keySelection,
&aliasList );
if ( err ){
Gripe( "\pAEPutParamDesc failed to put keySelection on open selection event" );
return err;
}
err = AEDisposeDesc( &aliasList );
if ( err ){
Gripe( "\pAEDisposeDesc failed" );
return err;
}
err = AESend( &launchEvent,
&replyEvent,
kAEWaitReply + kAECanInteract, /* Don't allow later switch */
kAENormalPriority,
kLaunchTimeout,
(IdleProcPtr)NULL,
(EventFilterProcPtr)NULL );
if ( err ){
Gripe( "\psend open selection event failed" );
return err;
}
err = AEDisposeDesc( &launchEvent );
if ( err ){
Gripe( "\pAEDisposeDesc failed" );
return err;
}
err = AEDisposeDesc( &replyEvent );
if ( err ){
Gripe( "\pAEDisposeDesc failed" );
return err;
}
return noErr;
}
#else
/* This makes the speller fail to process the batch event - I don't know why */
OSErr LaunchSpeller( AliasHandle aliasHdl )
{
LaunchParamBlockRec pb;
OSErr err;
FSSpec spellerSpec;
Boolean changed;
char *cPtr;
short i;
/* Make sure the speller can still be found */
err = ResolveAlias( (FSSpecPtr)NULL, aliasHdl, &spellerSpec, &changed );
if ( err ){
Gripe( "\pCannot locate speller" ); /* Might be user canceled AShare */
return err;
}
if ( changed ){
ChangedResource( aliasHdl );
WriteResource( aliasHdl );
}
cPtr = (char*)&pb;
for ( i = 0; i < sizeof( pb ); i++ ){
*cPtr++ = 0;
}
pb.launchBlockID = extendedBlock;
pb.launchEPBLength = extendedBlockLen;
pb.launchControlFlags = launchNoFileFlags + launchContinue /*+ launchDontSwitch*/;
pb.launchAppSpec = &spellerSpec;
pb.launchAppParameters = NULL;
err = LaunchApplication( &pb );
if ( err == memFullErr ){
Gripe( "\pThere is not enough memory to launch the Word Services server" );
return err;
}
if ( err ){
Gripe( "\pUnable to launch the Word Services server" );
return err;
}
return noErr;
}
#endif /* LAUNCH_BY_EVENT */